package cn.vehicle.service.config;

import cn.hutool.core.convert.Convert;
import cn.vehicle.domains.entity.SystemConfigEntity;
import cn.vehicle.exception.VehicleAccessException;
import cn.vehicle.mapper.SystemConfigMapper;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import jakarta.annotation.Resource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 系统配置服务类
 * <p>
 * 负责系统配置项的增删改查、缓存管理与类型转换。
 * 通过内置的本地缓存机制（Guava Cache）提升配置读取性能，
 * 并提供类型安全的配置访问方法。
 * </p>
 *
 * <p>主要职责：</p>
 * <ul>
 *   <li>从数据库读取系统配置项；</li>
 *   <li>根据配置项定义返回类型安全的值；</li>
 *   <li>维护配置项的本地缓存，自动过期与清理；</li>
 *   <li>支持配置新增、更新与缓存失效。</li>
 * </ul>
 */
@Service
public class SystemConfigService {

    @Resource
    private SystemConfigMapper systemConfigMapper;

    private final Cache<String, Object> cache = CacheBuilder.newBuilder()
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .maximumSize(10000L).build();


    /**
     * 获取配置项的值
     * <p>优先从本地缓存读取，若缓存不存在则从数据库加载。</p>
     *
     * @param item 配置项定义（包含 key、默认值、类型）
     * @param <T> 配置项的类型
     * @return 类型安全的配置值
     */
    public <T> T get(SystemConfigItem<T> item) {
        String key = item.getKey();

        // 尝试从本地缓存中读取配置项
        Object cached = cache.getIfPresent(key);
        // 若缓存命中，直接返回缓存值
        if (cached != null) {
            return item.getClazz().cast(cached);
        }

        // 缓存未命中，则从数据库加载配置值
        String value = systemConfigMapper.getValue(key);
        // 若数据库中无此配置项，则返回默认值并写入缓存
        if (value == null) {
            cache.put(key, item.getDefValue());
            return item.getDefValue();
        }

        // 将数据库中的字符串值转换为目标类型
        T realValue = Convert.convert(item.getClazz(), value);
        // 将转换后的值写入缓存，便于后续快速访问
        cache.put(key, realValue);
        return realValue;
    }

    /**
     * 移除指定配置项的缓存
     *
     * @param key 配置项的唯一标识符
     */
    public void removeCache(String key) {
        cache.invalidate(key);
    }

    /**
     * 根据配置键查询完整配置实体
     *
     * @param key 配置键
     * @return 系统配置实体对象
     */
    public SystemConfigEntity getEntityByKey(String key) {
        return systemConfigMapper.selectByKey(key);
    }

    /**
     * 新增系统配置
     * <p>若配置已存在则抛出重复异常。</p>
     *
     * @param systemConfigEntity 系统配置实体
     */
    public void insert(SystemConfigEntity systemConfigEntity) {

        try {
            // 尝试插入新的配置项记录
            systemConfigMapper.insert(systemConfigEntity);
        } catch (DuplicateKeyException e) {
            // 捕获唯一键冲突异常并转换为业务异常
            throw VehicleAccessException.DATE_EXIST.newInstance("配置已存在");
        }
    }

    /**
     * 更新系统配置项
     * <p>根据配置键更新对应值，若更新失败则抛出异常。</p>
     *
     * @param updateEntity 更新的配置实体
     */
    public void update(SystemConfigEntity updateEntity) {
        int i = systemConfigMapper.updateByKey(updateEntity);
        // 检查更新结果，若未更新到任何记录则视为配置不存在
        if (i != 1) {
            throw VehicleAccessException.DATE_NOT_EXIST.newInstance("更新配置未更新到数据");
        }
    }
}
